/**
 * @file        spi_sdcard.h
 * @brief       SD卡SPI模式驱动头文件
 * @details     基于SPI接口的SD卡驱动程序，支持SD V1.x/V2.x/SDHC/MMC卡
 *              实现SD卡的初始化、读写、状态检测等功能
 *
 * @par 功能特性:
 *              - 支持SD V1.x、SD V2.x、SDHC、MMC等多种卡类型
 *              - 基于SPI接口，硬件连接简单
 *              - 支持单扇区和多扇区读写操作
 *              - 兼容FATFS文件系统
 *              - 支持热插拔检测
 *
 * @par 硬件连接:
 *              SD卡引脚    STM32引脚    功能说明
 *              CS          PA3          片选信号
 *              MOSI        PA7          主机输出从机输入
 *              MISO        PA6          主机输入从机输出
 *              SCK         PA5          串行时钟
 *              VCC         3.3V         电源正极
 *              GND         GND          电源负极
 *
 * @par 移植说明:
 *              移植时需要实现spi1_read_write_byte()和spi1_set_speed()函数
 *              根据实际硬件连接修改SD_CS_GPIO_PORT和SD_CS_GPIO_PIN定义
 */

#ifndef __SPI_SDCARD_H
#define __SPI_SDCARD_H

#include "./BSP/SPI/bsp_spi.h"
#include "./SYSTEM/sys/sys.h"

/* ----------------------------------- SD卡硬件端口定义 -------------------------------------- */

/**
 * @brief SD卡片选引脚配置
 * @note  SD卡通过SPI接口通信，需要片选信号控制通信时序
 *        片选信号低电平有效，高电平释放SPI总线
 */
#define SD_CS_GPIO_PORT                 GPIOA                                       /* SD卡片选引脚端口 */
#define SD_CS_GPIO_PIN                  GPIO_PIN_3                                  /* SD卡片选引脚号 */
#define SD_CS_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* SD卡片选引脚时钟使能 */


/**
 * @brief SD卡SPI操作函数宏定义
 * @note  移植时需要根据实际SPI驱动实现以下函数:
 *        - spi1_read_write_byte(): SPI数据读写函数
 *        - spi1_set_speed(): SPI速度设置函数
 *        SD卡初始化时使用低速模式，初始化完成后切换到高速模式
 */
#define sd_spi_read_write_byte(x)       spi1_read_write_byte(x)             /* SD卡SPI数据读写函数 */
#define sd_spi_speed_low()              spi1_set_speed(SPI_SPEED_256)       /* SD卡SPI低速模式(281.25KHz) */
#define sd_spi_speed_high()             spi1_set_speed(SPI_SPEED_2)         /* SD卡SPI高速模式(36MHz) */


/**
 * @brief SD卡片选信号控制宏
 * @param x 片选状态: 1-释放片选(高电平), 0-选中SD卡(低电平)
 * @note  SD卡SPI通信前需要先拉低片选信号，通信结束后拉高释放总线
 *        多个SPI设备共用总线时，通过片选信号区分不同设备
 */
#define SD_CS(x)   do{ x ? \
                      HAL_GPIO_WritePin(SD_CS_GPIO_PORT, SD_CS_GPIO_PIN, GPIO_PIN_SET) : \
                      HAL_GPIO_WritePin(SD_CS_GPIO_PORT, SD_CS_GPIO_PIN, GPIO_PIN_RESET); \
                   }while(0)    /* SD卡片选信号控制 */

/* ----------------------------------- SD卡状态和类型定义 ----------------------------------- */

/**
 * @brief SD卡操作返回值定义
 * @note  所有SD卡操作函数的返回值标准
 */
#define SD_OK           0                                                   /* 操作成功 */
#define SD_ERROR        1                                                   /* 操作失败 */

/**
 * @brief SD卡类型定义
 * @note  根据SD卡初始化过程中的响应来识别卡类型
 *        不同类型的卡在容量计算和地址模式上有差异
 */
#define SD_TYPE_ERR     0X00                                                /* 错误卡类型/无卡 */
#define SD_TYPE_MMC     0X01                                                /* MMC卡 */
#define SD_TYPE_V1      0X02                                                /* SD V1.x标准容量卡 */
#define SD_TYPE_V2      0X04                                                /* SD V2.0标准容量卡 */
#define SD_TYPE_V2HC    0X06                                                /* SD V2.0高容量卡(SDHC) */

/* SD卡 命令定义 */
#define CMD0    (0)             /* 复位命令，使SD卡进入空闲状态 */
#define CMD1    (1)             /* 发送操作条件命令(MMC卡专用) */
#define ACMD41  (0x80 + 41)     /* 发送操作条件命令(SD卡专用) */
#define CMD8    (8)             /* 发送接口条件命令，检查电压范围 */
#define CMD9    (9)             /* 读取CSD寄存器(卡特定数据) */
#define CMD10   (10)            /* 读取CID寄存器(卡识别数据) */
#define CMD12   (12)            /* 停止传输命令，结束多块读写 */
#define ACMD13  (0x80 + 13)     /* 读取SD卡状态 */
#define CMD16   (16)            /* 设置块长度命令 */
#define CMD17   (17)            /* 读取单个数据块 */
#define CMD18   (18)            /* 读取多个数据块 */
#define CMD23   (23)            /* 设置块计数命令(MMC卡) */
#define ACMD23  (0x80 + 23)     /* 设置写入块擦除计数(SD卡) */
#define CMD24   (24)            /* 写入单个数据块 */
#define CMD25   (25)            /* 写入多个数据块 */
#define CMD32   (32)            /* 设置擦除块起始地址 */
#define CMD33   (33)            /* 设置擦除块结束地址 */
#define CMD38   (38)            /* 执行擦除操作 */
#define CMD55   (55)            /* 应用命令前缀，后续发送ACMD */
#define CMD58   (58)            /* 读取OCR寄存器(操作条件寄存器) */


/* ----------------------------------- 全局变量声明 ----------------------------------------- */

/**
 * @brief SD卡类型全局变量
 * @note  保存当前检测到的SD卡类型，用于后续读写操作中的地址转换
 *        在sd_init()函数中设置，其他函数根据此变量判断卡类型
 */
extern uint8_t  sd_type;

/* ----------------------------------- 函数声明 ---------------------------------------------- */

/* 内部静态函数声明 */
static void sd_spi_init(void);                                      /* SD卡SPI硬件层初始化 */
static void sd_deselect(void);                                      /* SD卡取消选中，释放SPI总线 */
static uint8_t sd_select(void);                                     /* SD卡选中，等待卡准备就绪 */
static uint8_t sd_wait_ready(void);                                 /* 等待SD卡准备就绪 */
static uint8_t sd_get_response(uint8_t response);                   /* 等待SD卡特定响应 */
static uint8_t sd_send_cmd(uint8_t cmd, uint32_t arg);              /* 向SD卡发送命令 */
static uint8_t sd_send_block(uint8_t *buf, uint8_t cmd);            /* 向SD卡发送数据块 */
static uint8_t sd_receive_data(uint8_t *buf, uint16_t len);         /* 从SD卡接收数据 */

/* 外部接口函数声明 */
uint8_t sd_init(void);                                              /* SD卡初始化 */
uint32_t sd_get_sector_count(void);                                 /* 获取SD卡总扇区数 */
uint8_t sd_get_status(void);                                        /* 获取SD卡状态 */
uint8_t sd_get_cid(uint8_t *cid_data);                              /* 获取SD卡CID信息 */
uint8_t sd_get_csd(uint8_t *csd_data);                              /* 获取SD卡CSD信息 */
uint8_t sd_read_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt);  /* SD卡扇区读取(fatfs/usb调用) */
uint8_t sd_write_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt); /* SD卡扇区写入(fatfs/usb调用) */
void show_sdcard_info(void);                                        /* 打印SD卡信息 */

#endif
